home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 424_01 / ed_157 / ftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-23  |  28.5 KB  |  1,201 lines

  1. /*
  2.  * Copyright (C) 1992 by Rush Record
  3.  * Copyright (C) 1993 by Charles Sandmann (sandmann@clio.rice.edu)
  4.  * 
  5.  * This file is part of ED.
  6.  * 
  7.  * ED is free software; you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation.
  9.  * 
  10.  * ED is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  11.  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  12.  * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  13.  * 
  14.  * You should have received a copy of the GNU General Public License along with ED
  15.  * (see the file COPYING).  If not, write to the Free Software Foundation, 675
  16.  * Mass Ave, Cambridge, MA 02139, USA.
  17.  */
  18. #include "opsys.h"
  19.  
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23.  
  24. #ifndef NO_FTP
  25. #include "unistd.h"
  26. #include "memory.h"
  27. #include "file.h"
  28. #include <signal.h>
  29. #include <errno.h>
  30.  
  31. #ifdef VMS
  32. #define NO_FTP_FILE        /* CWS - 11/93 */
  33.  
  34. #ifdef MULTINET
  35. #include "multinet_root:[multinet.include.sys]types.h"
  36. #include "multinet_root:[multinet.include.sys]socket.h"
  37. #include "multinet_root:[multinet.include.netinet]in.h"
  38. #include "multinet_root:[multinet.include]netdb.h"
  39. #define read socket_read
  40. #define write socket_write
  41. #define close socket_close
  42. #endif
  43.  
  44. #ifdef WOLLONGONG
  45. #include <in.h>
  46. #include <netdb.h>
  47. #include <socket.h>
  48. #include <inet.h>
  49. #define read netread
  50. #define write netwrite
  51. #define close netclose
  52. #endif
  53.  
  54. #ifdef UCX
  55. #include <in.h>
  56. #include <netdb.h>
  57. #include <socket.h>
  58. #include <inet.h>
  59. #endif
  60.  
  61. #else
  62.  
  63. #ifdef WIN32
  64. #define NO_FTP_FILE
  65. #include <winsock.h>
  66. #define read(a,b,c) recv(a,b,c,0)
  67. #define write(a,b,c) send(a,b,c,0)
  68. #define close closesocket
  69. #define errno WSAGetLastError()
  70. #define signal(a,b)
  71. static Char WSAInited = 0;
  72. #else
  73.  
  74. #include <setjmp.h>
  75. #include <netdb.h>
  76. #include <sys/socket.h>
  77. #include <arpa/inet.h>
  78. #include <netinet/in.h>
  79. #endif
  80. #endif
  81.  
  82. #include "ctyp_dec.h"
  83. #include "rec.h"
  84. #include "window.h"
  85. #include "ed_dec.h"
  86.  
  87. static Int inputchan = 0,outputchan = 0;
  88. #ifndef NO_FTP_FILE
  89. static FILE *inputfp = NULL;
  90. #endif
  91. static struct sockaddr_in controlsocket,datasocket;
  92. static Int controls,datas = 0,newdatas = 0;
  93. static Char curhost[64],curuser[64],curpass[64],curdir[128];
  94. static Char respbuf[512];
  95. static Int pipe_broken = 0;
  96. #ifdef VMS
  97. static Char systemtype = 'V';
  98. #else
  99. static Char systemtype = 'U';
  100. #endif
  101. static short ftpport = 0;
  102. static Int ascii_nbytes = 0;
  103. #ifdef DEBUG
  104. static FILE *dfp = NULL;
  105. #endif
  106.  
  107. #ifndef VMS
  108. extern char *sys_errlist[];
  109. #endif
  110. extern Char *netrc_password();
  111.  
  112. #else    /* NO_FTP */
  113. /******************************************************************************\
  114. |Routine: ftp_system
  115. |Callby: edit load_file
  116. |Purpose: Returns U for unix server, M for msdos, W for WNT, O for os/2, and
  117. |         V for VMS system.
  118. |Arguments:
  119. |    none
  120. \******************************************************************************/
  121. Int ftp_system(file)
  122. Char *file;
  123. {
  124. #ifdef VMS
  125.     return('V');
  126. #else
  127. #ifdef GNUOS2
  128.     return('O');
  129. #else
  130. #ifdef GNUDOS
  131.     return('M');
  132. #else
  133. #ifdef WNT
  134.     return('W');
  135. #else
  136.     return('U');
  137. #endif    /* WNT */
  138. #endif    /* GNUDOS */
  139. #endif    /* GNUOS2 */
  140. #endif    /* VMS */
  141. }
  142. #endif    /* NO_FTP */
  143.  
  144. #ifndef NO_FTP
  145. /******************************************************************************\
  146. |Routine: ftp_close
  147. |Callby: ftp
  148. |Purpose: Cleans up when an error occurs.
  149. |Arguments:
  150. |    none
  151. \******************************************************************************/
  152. void ftp_close()
  153. {
  154.     if(inputchan)
  155.     {
  156.         close(inputchan);
  157. #ifndef NO_FTP_FILE
  158.         fclose(inputfp);
  159. #endif
  160.         inputchan = 0;
  161.     }
  162.     if(outputchan)
  163.     {
  164.         close(outputchan);
  165.         outputchan = 0;
  166.     }
  167.     if(controls)
  168.     {
  169.         close(controls);
  170.         controls = 0;
  171.     }
  172.     if(datas)
  173.     {
  174.         close(datas);
  175.         datas = 0;
  176.     }
  177.     if(newdatas)
  178.     {
  179.         close(newdatas);
  180.         newdatas = 0;
  181.     }
  182.     curhost[0] = '\0';    /* prevent assumption of an open connection */
  183.     respbuf[0] = '\0';    /* prevent subsequent failure from reporting old error message */
  184.     signal(SIGPIPE,SIG_DFL);
  185. }
  186.  
  187. /******************************************************************************\
  188. |Routine: ftp_broken
  189. |Callby: signal mechanism
  190. |Purpose: Handles SIGPIPE while FTP connections are open.
  191. |Arguments:
  192. |    none
  193. \******************************************************************************/
  194. void ftp_broken(i)
  195. Int i;
  196. {
  197. #ifdef DEBUG
  198.     fprintf(dfp,"ftp_broken was called.\n");
  199.     fflush(dfp);
  200. #endif
  201.     ftp_close();
  202.     pipe_broken = 1;
  203.     return;
  204. }
  205.  
  206. /******************************************************************************\
  207. |Routine: ftp_reopen
  208. |Callby: ftp_command
  209. |Purpose: Reopens a connection that has been closed by the server.
  210. |Arguments:
  211. |    none
  212. \******************************************************************************/
  213. Int ftp_reopen()
  214. {
  215.     Char buf[512],chost[512],cuser[512];
  216.     
  217. #ifdef DEBUG
  218.     fprintf(dfp,"Trying to reopen the connection...\n");
  219.     fflush(dfp);
  220. #endif
  221.     strcpy(chost,curhost);
  222.     strcpy(cuser,curuser);
  223.     ftp_close();
  224.     if(!ftp_open(chost,cuser))
  225.         return(0);
  226.     sprintf(buf,"CWD %s",curdir);
  227.     if(!ftp_command(buf))
  228.         return(0);
  229.     return(1);
  230. }
  231.  
  232. /******************************************************************************\
  233. |Routine: ftp_abort
  234. |Callby: load_file
  235. |Purpose: Aborts FTP transfer.
  236. |Arguments:
  237. |    none
  238. \******************************************************************************/
  239. void ftp_abort()
  240. {
  241.     Char buf[512];
  242.  
  243. /*
  244.       By convention the sequence [IP, Synch] is to be used as such a
  245.       signal.  For example, suppose that some other protocol, which uses
  246.       TELNET, defines the character string STOP analogously to the
  247.       TELNET command AO.  Imagine that a user of this protocol wishes a
  248.       server to process the STOP string, but the connection is blocked
  249.       because the server is processing other commands.  The user should
  250.       instruct his system to:
  251.  
  252.          1. Send the TELNET IP character;
  253.  
  254.          2. Send the TELNET SYNC sequence, that is:
  255.  
  256.             Send the Data Mark (DM) as the only character
  257.             in a TCP urgent mode send operation.
  258.  
  259.          3. Send the character string STOP; and
  260.  
  261.          4. Send the other protocol's analog of the TELNET DM, if any.
  262.  
  263.       The user (or process acting on his behalf) must transmit the
  264.       TELNET SYNCH sequence of step 2 above to ensure that the TELNET IP
  265.       gets through to the server's TELNET interpreter.
  266.  
  267.    The following are the defined TELNET commands.  Note that these codes
  268.    and code sequences have the indicated meaning only when immediately
  269.    preceded by an IAC.
  270.  
  271.       NAME               CODE              MEANING
  272.  
  273.       SE                  240    End of subnegotiation parameters.
  274.       NOP                 241    No operation.
  275.       Data Mark           242    The data stream portion of a Synch.
  276.                                  This should always be accompanied
  277.                                  by a TCP Urgent notification.
  278.       Break               243    NVT character BRK.
  279.       Interrupt Process   244    The function IP.
  280. After the abort, we get from server either success, or 426 err followed by success.
  281. */
  282.     if(newdatas)
  283.     {
  284. #ifdef DEBUG
  285.         fprintf(dfp,"Aborting transfer.\n");
  286.         fflush(dfp);
  287. #endif
  288.         sprintf(buf,"%c%c",255,244);    /* this is the IP command */
  289.         write(outputchan,buf,strlen(buf));
  290.         sprintf(buf,"%c%c",255,242);    /* this is the DM command */
  291.         send(outputchan,buf,2,MSG_OOB);
  292.         sprintf(buf,"ABOR\r\n");    /* this is the ABORT command */
  293.         write(outputchan,buf,strlen(buf));
  294.         do
  295.         {
  296.             read_resp(buf);    /* there may be one or two responses, depending on whether the transfer just completed */
  297. #ifdef DEBUG
  298.             fprintf(dfp,"abort response:%s\n",buf);
  299.             fflush(dfp);
  300. #endif
  301.         } while(strncmp(buf,"226",strlen("226")));
  302.         close(newdatas);
  303.         newdatas = 0;
  304.         ascii_nbytes = 0;    /* so the next transferred file doesn't appear to contain data from this file */
  305.         bell();
  306.     }
  307. }
  308.  
  309. /******************************************************************************\
  310. |Routine: ftp_system
  311. |Callby: edit load_file
  312. |Purpose: Returns U for unix server and V for VMS server.
  313. |Arguments:
  314. |    none
  315. \******************************************************************************/
  316. Int ftp_system(file)
  317. Char *file;
  318. {
  319.     if(host_in_name(file))
  320.         return(systemtype);
  321. #ifdef VMS
  322.     return('V');
  323. #else
  324. #ifdef GNUOS2
  325.     return('O');
  326. #else
  327. #ifdef GNUDOS
  328.     return('M');
  329. #else
  330. #ifdef WNT
  331.     return('W');
  332. #else
  333.     return('U');
  334. #endif    /* WNT */
  335. #endif    /* GNUDOS */
  336. #endif    /* GNUOS2 */
  337. #endif    /* VMS */
  338. }
  339.  
  340. /******************************************************************************\
  341. |Routine: last_message
  342. |Callby: read_in_diredit
  343. |Purpose: Returns the last message from the FTP server.
  344. |Arguments:
  345. |    none
  346. \******************************************************************************/
  347. Char *last_message()
  348. {
  349.     return(respbuf);
  350. }
  351.  
  352. /******************************************************************************\
  353. |Routine: dump_message
  354. |Callby: ftp_binary ftp_put_binary ftp_put_record ftp_record
  355. |Purpose: Dumps the "transfer complete" message.
  356. |Arguments:
  357. |    none
  358. \******************************************************************************/
  359. void dump_message()
  360. {
  361.     Char resp[4],buf[256];
  362.  
  363.     read_resp(buf);
  364.     if(buf[3] == '-')
  365.     {
  366.         memcpy(resp,buf,3);
  367.         while(1)
  368.         {
  369.             if(!strncmp(buf,resp,3))
  370.                 if(buf[3] == ' ')
  371.                     break;
  372.             if(read_resp(buf) < 0)
  373.                 break;
  374.         }
  375.     }
  376. }
  377.     
  378. /******************************************************************************\
  379. |Routine: ftp_cwd
  380. |Callby: load_file
  381. |Purpose: Returns the current directory, necessary if server is VMS.
  382. |Arguments:
  383. |    none
  384. \******************************************************************************/
  385. Char *ftp_cwd()
  386. {
  387.     return(curdir);
  388. }
  389.  
  390. /******************************************************************************\
  391. |Routine: read_resp
  392. |Callby: ftp_open open_data_path
  393. |Purpose: Reads a response from the control path (terminated by CRLF). Returns
  394. |         the number of bytes in the response.
  395. |Arguments:
  396. |    buf is the returned response from the ftp server.
  397. \******************************************************************************/
  398. Int read_resp(buf)
  399. Char *buf;
  400. {
  401.     Char *p;
  402.     
  403.     p = buf;
  404.     *p = '\0';
  405.     if(!inputchan)
  406.         return(-1);
  407. #ifndef NO_FTP_FILE
  408.     if(!fgets(buf,512,inputfp))
  409.         return(-1);
  410.     return(strlen(buf));
  411. #else
  412.     while(1)
  413.     {
  414.         if(read(inputchan,p,1) != 1)
  415.             return(-1);
  416.         if(*p++ == '\n')
  417.         {
  418.             *p++ = '\0';
  419.             return(p - buf);
  420.         }
  421.     }
  422. #endif
  423. }
  424.  
  425. /******************************************************************************\
  426. |Routine: ftp_error
  427. |Callby: ftp
  428. |Purpose: Reports errors involving sockets.
  429. |Arguments:
  430. |    string is the error message.
  431. \******************************************************************************/
  432. Int ftp_error(string)
  433. Char *string;
  434. {
  435.     Char buf[512],*errmsg;
  436.     
  437.     sprintf(buf,"%s, errno=%d",string,errno);
  438.     slip_message(respbuf);
  439.     ftp_close();
  440.     slip_message(buf);
  441. #ifndef VMS
  442.     errmsg = (Char *)sys_errlist[errno];
  443. #else
  444.     errmsg = strerror(errno,vaxc$errno);
  445. #endif
  446.     slip_message(errmsg);
  447.     wait_message();
  448.     return(0);
  449. }
  450.  
  451. /******************************************************************************\
  452. |Routine: open_data_path
  453. |Callby: ftp_data
  454. |Purpose: Establishes a data connection for file transfer, directory listing.
  455. |Arguments:
  456. |    none
  457. \******************************************************************************/
  458. Int open_data_path()
  459. {
  460.     Int i;
  461.     Uchar *address,*port;
  462.     Char buf[256];
  463.     
  464.     if((datas = socket(AF_INET,SOCK_STREAM,0)) < 0)
  465.         return(ftp_error("Couldn't create socket"));
  466.     datasocket = controlsocket;    /* copy everything from control socket */
  467.     datasocket.sin_port = 0;
  468.     if(bind(datas,(struct sockaddr *)&datasocket,sizeof(datasocket)) < 0)
  469.         return(ftp_error("Couldn't bind socket"));
  470.     i = sizeof(datasocket);
  471.     if(getsockname(datas,(struct sockaddr *)&datasocket,&i) < 0)
  472.         return(ftp_error("Couldn't get socket name"));
  473.     if(listen(datas,1))    /* listen for connection from server */
  474.         return(ftp_error("Couldn't listen on data socket"));
  475.     address = (Uchar *)&datasocket.sin_addr;    /* send PORT command to server */
  476.     port = (Uchar *)&datasocket.sin_port;
  477.     sprintf(buf,"PORT %d,%d,%d,%d,%d,%d\r\n",address[0],address[1],address[2],address[3],port[0],port[1]);
  478. open_retry:
  479.     if(!outputchan || write(outputchan,buf,strlen(buf)) <= 0)
  480.     {
  481.         if(pipe_broken)
  482.             if(ftp_reopen())
  483.                 goto open_retry;
  484.         return(ftp_error("Couldn't send PORT command"));
  485.     }
  486.     if(read_resp(buf) < 0)
  487.         return(0);    /* read response to port command */
  488.     return(datas);
  489. }
  490.  
  491. /******************************************************************************\
  492. |Routine: ftp_open
  493. |Callby: ftp load_file output_file
  494. |Purpose: Sets up the control connection for ftp. Returns 0 for failure, else 1.
  495. |Arguments:
  496. |    host is the host on which the server is to run.
  497. |    user is the username to use.
  498. \******************************************************************************/
  499. Int ftp_open(host,user)
  500. Char *host,*user;
  501. {
  502.     Int i;
  503.     Char buf[256],passw[64],*p,*q,resp[4],initrespbuf[512];
  504.     struct hostent *host_info_ptr;
  505.  
  506. #ifdef WIN32
  507.     if(!WSAInited)
  508.     {
  509.         WSADATA WSAData;
  510.         Int status;
  511.  
  512.         if((status = WSAStartup(MAKEWORD(1,1), &WSAData)))
  513.         {
  514.             sprintf(buf,"Error opening WinSock library: %d",status);
  515.             slip_message(buf);
  516.             wait_message();
  517.             return(0);
  518.         }
  519.         WSAInited = 1;
  520.     }
  521. #endif
  522.  
  523. #ifdef DEBUG
  524.     if(!dfp)
  525.         dfp = fopen("ftp.dat","w");
  526.     fprintf(dfp,"ftp_open(%s,%s)\n",host,user);
  527.     fflush(dfp);
  528. #endif
  529.     pipe_broken = 0;    /* this is a flag that the remote server has timed out the connection */
  530.     if(!strcmp(host,curhost) && !strcmp(user,curuser))    /* if already connected, continue (blithely) */
  531.         return(1);
  532.     if(inputchan && outputchan)    /* terminate existing connection */
  533.     {
  534.         ftp_command("ABOR");
  535.         ftp_command("QUIT");
  536.         ftp_close();
  537.     }
  538. /* get info about service (this only need be called only once) */
  539.     if(!ftpport)
  540.         ftpport = htons(21);    /* CWS 11-93 */
  541. /* get host info */
  542.     memset(&controlsocket,0,sizeof(controlsocket));
  543.     if((controlsocket.sin_addr.s_addr = inet_addr((char *)host)) != -1)    /* check for numeric address */
  544.         controlsocket.sin_family = AF_INET;
  545.     else    /* non-numeric address */
  546.     {
  547.         if(!(host_info_ptr = gethostbyname(host)))
  548.         {
  549.             sprintf(buf,"'%s' is an unknown host.",host);
  550.             slip_message(buf);
  551.             wait_message();
  552.             return(0);
  553.         }
  554.         controlsocket.sin_family = host_info_ptr->h_addrtype;
  555.         memcpy(&controlsocket.sin_addr,host_info_ptr->h_addr_list[0],host_info_ptr->h_length);
  556.     }
  557.     controlsocket.sin_port = ftpport;
  558. /* create socket */
  559.     if((controls = socket(controlsocket.sin_family,SOCK_STREAM,0)) < 0)
  560.         return(ftp_error("Error creating socket for control path"));
  561. /* connect to ftp service */
  562.     while(1)
  563.         if(connect(controls,(struct sockaddr *)&controlsocket,sizeof(controlsocket)) != -1)
  564.             break;
  565.         else if(errno != EINTR)
  566.             return(ftp_error("Control connection failed"));
  567. /* prepare for server closing connection abruptly */
  568.     signal(SIGPIPE,ftp_broken);
  569. /* retrieve complete socket info */
  570.     i = sizeof(controlsocket);
  571.     if(getsockname(controls,(struct sockaddr *)&controlsocket,&i) < 0)
  572.         return(ftp_error("Couldn't get control socket name"));
  573.     outputchan = inputchan = controls;
  574. #ifndef NO_FTP_FILE
  575.     inputfp = fdopen(inputchan,"r");
  576. #endif
  577. /* read the connect message(s) */
  578.     if(read_resp(buf) < 0)    /* read the connect message */
  579.         return(ftp_error("Couldn't connect to the FTP server"));
  580. /* check for sparc error message */
  581.     if(!strncmp(buf,"ld.so",5))
  582.         read_resp(buf);
  583. /* check for immediate rejection */
  584.     if(buf[0] > '3')
  585.     {
  586.         slip_message("Unable to do FTP with that host, it said:");
  587.         if(buf[3] == '-')
  588.         {
  589.             memcpy(resp,buf,3);
  590.             while(1)
  591.             {
  592.                 if((p = strchr(buf,'\r')))
  593.                     *p = '\0';
  594.                 if((p = strchr(buf,'\n')))
  595.                     *p = '\0';
  596.                 slip_message(buf);
  597.                 if(!strncmp(buf,resp,3))
  598.                     if(buf[3] == ' ')
  599.                         break;
  600.                 if(read_resp(buf) < 0)
  601.                     break;
  602.             }
  603.         }
  604.         else
  605.         {
  606.             if((p = strchr(buf,'\r')))
  607.                 *p = '\0';
  608.             if((p = strchr(buf,'\n')))
  609.                 *p = '\0';
  610.             slip_message(buf);
  611.         }
  612.         wait_message();
  613.         ftp_close();
  614.         return(0);
  615.     }
  616.     strcpy(initrespbuf,buf);    /* save so we can check for FTPSERV below */
  617. /* handle multi-line greeting */
  618.     if(buf[3] == '-')
  619.     {
  620.         memcpy(resp,buf,3);
  621.         while(1)
  622.         {
  623.             if(!strncmp(buf,resp,3))
  624.                 if(buf[3] == ' ')
  625.                     break;
  626.             if(read_resp(buf) < 0)
  627.                 return(ftp_error("Couldn't connect to the FTP server"));
  628.         }
  629.     }
  630. /* log in to the host */
  631.     sprintf(buf,"USER %s",user);
  632.     if(!ftp_command(buf))
  633.     {
  634.         slip_message(respbuf);
  635.         ftp_close();
  636.         wait_message();
  637.         return(0);
  638.     }
  639.     if(respbuf[0] == '3')
  640.     {
  641. /* deal with passwords */
  642.         if(strlen(p = netrc_password()))    /* get password from .netrc if possible */
  643.             strcpy(passw,p);
  644.         else if(!strcmp(user,"anonymous"))
  645.         {
  646.             gethostname(buf,sizeof(buf));
  647. /*            if(!strchr(buf,'.'))  then add domain... */
  648. #ifdef UCX
  649.             if((p = getenv("UCX$BIND_DOMAIN")))
  650.             {
  651.                 strcat(buf,".");
  652.                 strcat(buf,p);
  653.             }
  654. #endif
  655. #ifdef WOLLONGONG
  656.             if((p = getenv("INET_DOMAIN_NAME")))
  657.             {
  658.                 strcat(buf,".");
  659.                 strcat(buf,p);
  660.             }
  661. #endif
  662.  
  663. #ifdef CUSERID_ENV
  664.             sprintf(passw,"%s@%s",getenv(CUSERID_ENV),buf);
  665. #else
  666.             sprintf(passw,"%s@%s",cuserid(NULL),buf);
  667. #endif
  668.         }
  669.         else
  670.         {
  671.             inquire_hidden(1);    /* prompt the user for the password */
  672.             inquire("Password",passw,sizeof(passw),1);
  673.             inquire_hidden(0);
  674.         }
  675.         sprintf(buf,"PASS %s",passw);
  676.         if(!ftp_command(buf))
  677.             goto badlogin;
  678.     }
  679. /* determine the system type */
  680.     if(!ftp_command("SYST"))
  681.     {
  682.         if(!ftp_command("PWD"))
  683.             if(!ftp_command("XPWD"))
  684.                 goto badlogin;
  685.         if(strstr(respbuf,"defined"))
  686.             systemtype = 'I';
  687.         else if(strchr(respbuf,'/'))
  688.             systemtype = 'U';
  689.         else
  690.             systemtype = 'V';
  691.     }
  692.     else
  693.     {
  694.         if(respbuf[4] == '\'')
  695.             systemtype = 'U';
  696.         else
  697.         {
  698.             if(respbuf[4] == 'V')
  699.             {
  700.                 if(respbuf[6] == 'S')
  701.                     systemtype = 'V';
  702.                 else
  703.                     systemtype = 'I';    /* this is a VM system, pretend it's a VMS system */
  704.             }
  705.             else if(!strncmp(respbuf + 4,"MTS",strlen("MTS")))
  706.                 systemtype = 'T';
  707.             else if(!strncmp(initrespbuf + 4,"FTPSERV",strlen("FTPSERV")))
  708.                 systemtype = 'I';
  709.             else
  710.                 systemtype = respbuf[4];
  711.         }
  712.     }
  713. /* make AIX look like UNIX */
  714.     if(systemtype == 'A')
  715.         systemtype = 'U';
  716. /* get the current directory */
  717.     if(!ftp_command("PWD"))
  718.         if(!ftp_command("XPWD"))
  719.             goto badlogin;
  720.     if(strstr(respbuf,"defined"))
  721.         curdir[0] = '\0';
  722.     else
  723.     {
  724.         strcpy(curdir,respbuf + 4);
  725.         if(systemtype == 'T')    /* MTS reports cwd as DISKNAME.: */
  726.         {
  727.  
  728.             if(curdir[0] == '"')    /* strip quotes if present */
  729.             {
  730.                 for(p = curdir + 1;*p != '"';p++)
  731.                     *(p - 1) = *p;
  732.                 *p = '\0';
  733.             }
  734.             if((p = strchr(curdir,':')))
  735.                 if(*(p - 1) == '.')
  736.                     p--;
  737.             *p = '\0';
  738.             sprintf(buf,"[%s]",curdir);
  739.             strcpy(curdir,buf);
  740.         }
  741.         else
  742.         {
  743.             for(p = curdir;!isspace(*p);p++);    /* trim trailing space */
  744.             *p = '\0';
  745.             if(curdir[0] == '"')    /* strip quotes if present */
  746.             {
  747.                 for(q = curdir,p = q + 1;*p != '"';*q++ = *p++);
  748.                 *q++ = '\0';
  749.             }
  750.             if(systemtype == 'I' && curdir[0] != '[')    /* make VM look like VMS */
  751.             {
  752.                 sprintf(buf,"[%s]",curdir);
  753.                 strcpy(curdir,buf);
  754.             }
  755.         }
  756.     }
  757. /* save params for comparison on next try */
  758.     strcpy(curhost,host);
  759.     strcpy(curuser,user);
  760.     strcpy(curpass,passw);
  761.     return(1);
  762. /* a problem logging in */
  763. badlogin:
  764.     if((p = strchr(respbuf,'\r')))
  765.         *p = '\0';
  766.     if((p = strchr(respbuf,'\n')))
  767.         *p = '\0';
  768.     slip_message(respbuf);
  769.     ftp_close();
  770.     wait_message();
  771.     return(0);
  772. }
  773.  
  774. /******************************************************************************\
  775. |Routine: ftp_command
  776. |Callby: ftp load_file output_file
  777. |Purpose: Sends a command to the ftp server, and gets the response.
  778. |Arguments:
  779. |    cmd is the command to send.
  780. \******************************************************************************/
  781. Int ftp_command(cmd)
  782. Char *cmd;
  783. {
  784.     static Int recurse = 0;    /* flags a recursive call */
  785.     static Char *retrypass = "pass rhr";
  786.  
  787.     Int l;
  788.     Char resp[4],buf[256],chost[256],cuser[256],*p;
  789.     
  790. #ifdef DEBUG
  791.     if(!dfp)
  792.         dfp = fopen("ftp.dat","w");
  793.     fprintf(dfp,"ftp_command(%s)\n",cmd);
  794.     fflush(dfp);
  795. #endif
  796. /* be sure the channels are open */
  797.     if(!inputchan)
  798.         return(0);
  799. /* send the command */
  800. retry:
  801.     strcpy(respbuf,cmd);
  802.     strcat(respbuf,"\r\n");
  803.     if((l = strlen(respbuf)))
  804.         if(!outputchan || write(outputchan,respbuf,l) <= 0)
  805.         {
  806. /* try to reopen the connection */
  807.             if(pipe_broken)
  808.                 if(ftp_reopen())
  809.                     goto retry;
  810.             return(ftp_error("It appears the remote host has closed the connection"));
  811.         }
  812. /* get the response */
  813.     if(read_resp(respbuf) < 0)
  814.     {
  815. #ifdef DEBUG
  816.         fprintf(dfp,"read_resp response:%s\n",respbuf);
  817.         fflush(dfp);
  818. #endif
  819.         strcpy(chost,curhost);
  820.         strcpy(cuser,curuser);
  821.         ftp_close();
  822.         if(!(ftp_open(chost,cuser)))
  823.             return(0);
  824.         goto retry;
  825.     }
  826. #ifdef DEBUG
  827.     fprintf(dfp,"response:%s\n",respbuf);
  828.     fflush(dfp);
  829. #endif
  830. /* remove \r\n if it's there */
  831.     if((p = strchr(respbuf,'\r')))
  832.         *p = '\0';
  833.     if((p = strchr(respbuf,'\n')))
  834.         *p = '\0';
  835. /* handle server timeout */
  836.     if(!strncmp(respbuf,"421",3))
  837.     {
  838. #ifdef DEBUG
  839.         fprintf(dfp,"server timeout\n");
  840.         fflush(dfp);
  841. #endif
  842.         if(!ftp_reopen())
  843.             return(ftp_error("It appears the remote host has closed the connection"));
  844.         goto retry;
  845.     }
  846. /* this is for servers that do not report nnn at the beginning of each record */
  847.     if(my_sscanf(respbuf,"%d",&l) != 1)
  848.     {
  849.         read_resp(respbuf);
  850.         ftp_close();
  851.         return(0);
  852.     }
  853.     if(!strncmp(cmd,"PASS",4) && strstr(respbuf,"BAD SYNTAX"))
  854.     {
  855.         cmd = retrypass;
  856.         goto retry;
  857.     }
  858.     if(!recurse && !strncmp(respbuf,"530",3) && strlen(curuser) && strncmp(cmd,"PASS",4))    /* user no longer logged in, relogin */
  859.     {
  860. #ifdef DEBUG
  861.         fprintf(dfp,"reopening connection with host = %s, user = %s\n",curhost,curuser);
  862.         fflush(dfp);
  863. #endif
  864.         recurse = 1;    /* prevent infinite recursion */
  865.         sprintf(buf,"USER %s",curuser);
  866.         if(!ftp_command(buf))
  867.         {
  868.             recurse = 0;
  869.             ftp_close();
  870.             return(0);
  871.         }
  872.         sprintf(buf,"PASS %s",curpass);
  873.         if(!ftp_command(buf))
  874.         {
  875.             recurse = 0;
  876.             ftp_close();
  877.             return(0);
  878.         }
  879.         sprintf(buf,"CWD %s",curdir);
  880.         if(!ftp_command(buf))
  881.         {
  882.             recurse = 0;
  883.             ftp_close();
  884.             return(0);
  885.         }
  886.         recurse = 0;
  887. /* send whatever command they wanted to send, again */
  888.         strcpy(respbuf,cmd);
  889.         strcpy(respbuf + strlen(respbuf),"\r\n");
  890.         if((l = strlen(respbuf)))
  891.             if(!outputchan || write(outputchan,respbuf,l) <= 0)
  892.                 return(ftp_error("I'm unable to send a command to the remote host"));
  893.         if(read_resp(respbuf) < 0)
  894.             return(0);
  895.     }
  896. /* handle multi-line response */
  897.     if(respbuf[3] == '-')
  898.     {
  899.         memcpy(resp,respbuf,3);
  900.         while(1)
  901.         {
  902.             if(!strncmp(respbuf,resp,3))
  903.                 if(respbuf[3] == ' ')
  904.                     break;
  905.             if(read_resp(respbuf) < 0)
  906.                 return(0);
  907. #ifdef DEBUG
  908.             fprintf(dfp,"continue:%s\n",respbuf);
  909.             fflush(dfp);
  910. #endif
  911.         }
  912.     }
  913. /* if it's a CWD command, save the directory name */
  914.     l = (respbuf[0] <= '3')? 1 : 0;
  915.     if(!strncmp(cmd,"CWD ",4) && l)
  916.         strcpy(curdir,cmd + 4);
  917.     return(l);
  918. }
  919.  
  920. /******************************************************************************\
  921. |Routine: ftp_data
  922. |Callby: load_file output_file
  923. |Purpose: Sends a command that requires a data connection (LIST, NLST, RETR).
  924. |Arguments:
  925. |    cmd is the command to send.
  926. \******************************************************************************/
  927. Int ftp_data(cmd)
  928. Char *cmd;
  929. {
  930.     Char lcmd[256],*p;
  931.     Int i;
  932.     
  933.     if(!(datas = open_data_path()))    /* open data path to server */
  934.         return(0);
  935.     strcpy(lcmd,cmd);    /* send command to server */
  936.     strcat(lcmd,"\r\n");
  937. retry_data:
  938.     if(!outputchan || write(outputchan,lcmd,strlen(lcmd)) <= 0)
  939.     {
  940.         if(pipe_broken)
  941.             if(ftp_reopen())
  942.                 goto retry_data;
  943.         return(ftp_error("The remote host appears to have closed the connection"));
  944.     }
  945. #ifdef DEBUG
  946.     fprintf(dfp,"data mode command:%s\n",cmd);
  947.     fflush(dfp);
  948. #endif
  949.     if(read_resp(lcmd) < 0)
  950.     {
  951.         sprintf(lcmd,"Unable to execute command '%s'",cmd);
  952.         return(ftp_error(lcmd));
  953.     }
  954.     if(lcmd[0] > '3')    /* we sort of pray here that server that has more to say shuts up nicely */
  955.     {
  956.         if((p = strchr(lcmd,'\r')))
  957.             *p = '\0';
  958.         if((p = strchr(lcmd,'\n')))
  959.             *p = '\0';
  960.         slip_message(lcmd);
  961.         wait_message();
  962.         return(0);
  963.     }
  964. /* set up socket for transfer, blocking in accept() until the server initiates the connection */
  965.     i = sizeof(datasocket);
  966.     if((newdatas = accept(datas,(struct sockaddr *)&datasocket,&i)) < 0)
  967.     {
  968.         newdatas = 0;
  969.         return(ftp_error("Couldn't accept connection"));
  970.     }
  971.     close(datas);
  972.     datas = 0;
  973.     return(1);
  974. }
  975.     
  976. /******************************************************************************\
  977. |Routine: ftp_record
  978. |Callby: load_file
  979. |Purpose: Reads a record from the data path.
  980. |Arguments:
  981. |    rec is the returned record.
  982. \******************************************************************************/
  983. Int ftp_record(rec)
  984. Char *rec;
  985. {
  986.     static Char buffer[128],*p;
  987.     Char *save_rec;
  988.     
  989.     if(!newdatas)
  990.         return(0);
  991.     save_rec = rec;
  992.     while(1)
  993.     {
  994.         if(ascii_nbytes-- > 0)
  995.         {
  996.             if((*rec++ = *p++) == '\n')
  997.             {
  998.                 *(rec - 2) = '\0';
  999. #ifdef DEBUG
  1000.                 fprintf(dfp,"ftp_record gets:%s\n",save_rec);
  1001.                 fflush(dfp);
  1002. #endif
  1003.                 return(1);
  1004.             }
  1005.         }
  1006.         else
  1007.         {
  1008.             if((ascii_nbytes = read(newdatas,buffer,sizeof(buffer))) <= 0)
  1009.             {
  1010.                 if(rec != save_rec)    /* we have a faulty file */
  1011.                 {
  1012.                     *p++ = '\n';
  1013.                     *p++ = '\0';
  1014. #ifdef DEBUG
  1015.                     fprintf(dfp,"ftp_record gets(faulty):%s\n",save_rec);
  1016.                     fflush(dfp);
  1017. #endif
  1018.                     return(1);
  1019.                 }
  1020.                 close(newdatas);
  1021.                 newdatas = 0;
  1022.                 dump_message();    /* dump the "transfer complete" message */
  1023.                 return(0);
  1024.             }
  1025.             p = buffer;
  1026.         }
  1027.     }
  1028. }
  1029.     
  1030. /******************************************************************************\
  1031. |Routine: ftp_ascii
  1032. |Callby: load_file
  1033. |Purpose: Reads a record from the data path.
  1034. |Arguments:
  1035. |    rec is the returned record.
  1036. |    len is the length of rec in bytes.
  1037. \******************************************************************************/
  1038. Int ftp_ascii(rec,len)
  1039. Char *rec;
  1040. Int len;
  1041. {
  1042.     static Char buffer[128],*p,*stop;
  1043.     Char *save_rec;
  1044.     
  1045.     if(!newdatas)
  1046.         return(0);
  1047.     save_rec = rec;
  1048.     stop = rec + len - 1;    /* last byte of buffer */
  1049.     while(1)
  1050.     {
  1051.         if(rec == stop)    /* the buffer is full, break the record here */
  1052.         {
  1053.             *stop = '\0';
  1054. #ifdef DEBUG
  1055.             fprintf(dfp,"ftp_ascii buffer fills\n");
  1056.             fflush(dfp);
  1057. #endif
  1058.             return(1);
  1059.         }
  1060.         if(ascii_nbytes-- > 0)
  1061.         {
  1062.             if((*rec++ = *p++) == '\n')    /* we have a record termination */
  1063.             {
  1064.                 if(rec - save_rec < 2)    /* if it's just \n, return zero-length string */
  1065.                     *save_rec = '\0';
  1066.                 else
  1067.                 {
  1068.                     if(*(rec - 2) == '\r')
  1069.                         *(rec - 2) = '\0';
  1070.                     else
  1071.                         *--rec = '\0';
  1072.                 }
  1073. #ifdef DEBUG
  1074.                 fprintf(dfp,"ftp_ascii gets:%s\n",save_rec);
  1075.                 fflush(dfp);
  1076. #endif
  1077.                 return(1);
  1078.             }
  1079.         }
  1080.         else
  1081.         {
  1082.             if((ascii_nbytes = read(newdatas,buffer,sizeof(buffer))) <= 0)
  1083.             {
  1084.                 if(rec != save_rec)    /* we have a faulty file */
  1085.                 {
  1086.                     *rec++ = '\n';
  1087.                     *rec++ = '\0';
  1088. #ifdef DEBUG
  1089.                     fprintf(dfp,"ftp_record gets(faulty):%s\n",save_rec);
  1090.                     fflush(dfp);
  1091. #endif
  1092.                     return(1);
  1093.                 }
  1094.                 close(newdatas);
  1095.                 newdatas = 0;
  1096.                 dump_message();    /* dump the "transfer complete" message */
  1097.                 return(0);
  1098.             }
  1099.             p = buffer;
  1100.         }
  1101.     }
  1102. }
  1103.     
  1104. /******************************************************************************\
  1105. |Routine: ftp_binary
  1106. |Callby: load_file
  1107. |Purpose: Reads a record from the data path, in binary mode.
  1108. |Arguments:
  1109. |    rec is the returned (usually 512-byte) record.
  1110. \******************************************************************************/
  1111. Int ftp_binary(rec)
  1112. Char *rec;
  1113. {
  1114.     Int l;
  1115.     
  1116.     if(!newdatas)
  1117.         return(0);
  1118.     if((l = read(newdatas,rec,512)) <= 0)
  1119.     {
  1120.         close(newdatas);
  1121.         newdatas = 0;
  1122.         dump_message();    /* dump the "transfer complete" message */
  1123.         return(0);
  1124.     }
  1125.     return(l);
  1126. }
  1127.  
  1128. /******************************************************************************\
  1129. |Routine: ftp_put_record
  1130. |Callby: read_in_diredit
  1131. |Purpose: Writes a record to the server.
  1132. |Arguments:
  1133. |    rec is the record to send. rec = NULL means write EOF.
  1134. \******************************************************************************/
  1135. Int ftp_put_record(rec)
  1136. Char *rec;
  1137. {
  1138.     Int l;
  1139.     
  1140.     if(!rec)    /* NULL argument signifies end-of-file */
  1141.     {
  1142.         close(newdatas);
  1143.         newdatas = 0;
  1144.         return(1);
  1145.     }
  1146.     l = strlen(rec);
  1147.     if(write(newdatas,rec,l) != l)
  1148.     {
  1149.         close(newdatas);
  1150.         newdatas = 0;
  1151.         dump_message();    /* dump the "transfer complete" message */
  1152.         return(0);
  1153.     }
  1154.     return(1);
  1155. }
  1156.     
  1157. /******************************************************************************\
  1158. |Routine: ftp_put_binary
  1159. |Callby: output_file
  1160. |Purpose: Writes binary data to the server.
  1161. |Arguments:
  1162. |    rec is the data to write.
  1163. |    bytes is how many to write. bytes = 0 means write EOF.
  1164. \******************************************************************************/
  1165. Int ftp_put_binary(rec,bytes)
  1166. Char *rec;
  1167. Int bytes;
  1168. {
  1169.     if(!bytes)    /* NULL argument signifies end-of-file */
  1170.     {
  1171.         close(newdatas);
  1172.         newdatas = 0;
  1173.         return(1);
  1174.     }
  1175.     if(write(newdatas,rec,bytes) != bytes)
  1176.     {
  1177.         close(newdatas);
  1178.         newdatas = 0;
  1179.         dump_message();    /* dump the "transfer complete" message */
  1180.         return(0);
  1181.     }
  1182.     return(1);
  1183. }
  1184. #endif    /* NO_FTP */
  1185.  
  1186. /******************************************************************************\
  1187. |Routine: ftp_unixy
  1188. |Callby: edit load_file
  1189. |Purpose: Returns 1 if the system is 'unixy'.
  1190. |Arguments:
  1191. |    system is the system type, one of V W M O U.
  1192. \******************************************************************************/
  1193. Int ftp_unixy(system)
  1194. Int system;
  1195. {
  1196.     if(strchr("WMOU",system))
  1197.         return(1);
  1198.     return(0);
  1199. }
  1200.  
  1201.